package com.kevin.spider.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.kevin.file.domain.entity.SysFileRelation;
import com.kevin.file.enums.BusinessType;
import com.kevin.file.service.FileService;
import com.kevin.file.service.SysFileRelationService;
import com.kevin.shop.domain.entity.ShopGoodSelection;
import com.kevin.shop.mapper.ShopGoodSelectionMapper;
import com.kevin.spider.convert.SpiderGoodConverter;
import com.kevin.spider.domain.bo.SpiderContext;
import com.kevin.spider.domain.bo.TaoBaoGood;
import com.kevin.spider.utils.ImageUtils;
import com.microsoft.playwright.ElementHandle;
import com.microsoft.playwright.Page;
import com.ruoyi.common.constant.HttpStatus;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static java.util.stream.Collectors.toList;

/**
 * @author 张帆
 * @date 2025/6/16 17:56
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SpiderTaoBaoService extends BaseSpiderService {
    private final ShopGoodSelectionMapper shopGoodSelectionMapper;
    private final FileService fileService;

    private final SysFileRelationService sysFileRelationService;

    private ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

    private final String templateUrl = "https://item.taobao.com/item.htm?id={0}";

    private final String loginUrl = "https://login.taobao.com/havanaone/login/login.htm";

    private final String indexUrl = "https://www.taobao.com/";

    private final Integer picWidth = 1512;

    private final Integer picHeight = 2016;

    private final Long loginCostTime = 30000L;

    private final Boolean waitLogin = true;

    private static Path DOWNLOAD_DIR = Paths.get("downloads");

    static {
        // 创建下载目录
        if (!Files.exists(DOWNLOAD_DIR)) {
            try {
                Files.createDirectories(DOWNLOAD_DIR);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @SneakyThrows
    public void spiderTaoBao(List<Long> selectionIds) {
        List<ShopGoodSelection> shopGoodSelections = shopGoodSelectionMapper.selectShopGoodSelectionsBySelectionId(selectionIds);
        SpiderContext spiderContext = buildSpiderContext();
        shopGoodSelections.forEach(shopGoodSelection -> spider(shopGoodSelection, spiderContext.getPage()));
        spiderContext.getPlaywright().close();
    }

    @SneakyThrows
    public void login(SpiderContext spiderContext) {
        if (waitLogin) {
            spiderContext.getPage().navigate(loginUrl);
            Thread.sleep(loginCostTime);
        }
    }

    public void spider(ShopGoodSelection shopGoodSelection, Page page) {
        TaoBaoGood good = spider(shopGoodSelection.getGoodId(), page);
        SpiderGoodConverter.INSTANCE.updateGoodSelection(shopGoodSelection, good);
        shopGoodSelectionMapper.updateShopGoodSelection(shopGoodSelection);

        if (CollectionUtil.isNotEmpty(good.getGoodMainPics())) {
            good.getGoodMainPics().forEach(goodMainPic -> {
                Long fileId = fileService.uploadFile(goodMainPic, StrUtil.join(StrUtil.UNDERLINE, good.getGoodId(), goodMainPic.getName()), BusinessType.TAO_BAO.getCode());
                SysFileRelation sysFileRelation = new SysFileRelation();
                sysFileRelation.setBusinessId(shopGoodSelection.getSelectionId());
                sysFileRelation.setFileId(fileId);
                sysFileRelation.setBusinessType(BusinessType.SELECTION.getCode());
                sysFileRelationService.insertSysFileRelation(sysFileRelation);
            });
        }
    }

    @SneakyThrows
    public TaoBaoGood spider(String goodId, Page page) {
        TaoBaoGood good = new TaoBaoGood();
        String url = MessageFormat.format(templateUrl, goodId);
        page.navigate(url);

        ElementHandle titleElement = getTitle(page);
        List<ElementHandle> mainPicElements = getMainPics(page);

        // 使用虚拟线程池进行并发下载
        HttpClient client = HttpClient.newHttpClient();
        List<Future<Optional<File>>> futures = new ArrayList<>();
        for (int i = 1; i <= mainPicElements.size(); i++) {
            ElementHandle imageElement = mainPicElements.get(i - 1);
            String src = (String) imageElement.getProperty("src").jsonValue();
            // 处理相对路径
            if (!src.startsWith("http")) {
                String baseUrl = page.url();
                src = java.net.URI.create(baseUrl).resolve(src).toString();
            }
            String imgSrc = src;
            String imgName = "主图" + i + StrUtil.DOT + ImageUtils.PNG;
            Future<Optional<File>> future = executor.submit(() -> downloadImage(client, imgSrc, imgName));
            futures.add(future);
        }

        List<File> goodMainPics = futures.stream().map(e -> {
                    try {
                        return e.get();
                    } catch (InterruptedException | ExecutionException ex) {
                        throw new RuntimeException(ex);
                    }
                }).filter(Optional::isPresent)
                .map(Optional::get).collect(toList());

        good.setGoodId(goodId);
        good.setGoodName(titleElement.innerText());
        good.setGoodLink(url);
        good.setGoodMainPics(goodMainPics);
        return good;
    }

    private Optional<File> downloadImage(HttpClient client, String imageUrl, String imgName) {
        try {
            String filename = imageUrl.substring(imageUrl.lastIndexOf(StrUtil.SLASH) + 1);
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(imageUrl))
                    .build();
            Path imgPath = DOWNLOAD_DIR.resolve(filename);
            HttpResponse<Path> response = client.send(request, HttpResponse.BodyHandlers.ofFile(imgPath));
            Path targetPath = imgPath;
            File targetFile = targetPath.toFile();
            if (filename.endsWith(ImageUtils.WEBP)) {
                File imgFile = imgPath.toFile();
                targetPath = DOWNLOAD_DIR.resolve(imgName);
                targetFile = targetPath.toFile();
                ImageUtils.formatConvert(imgFile, targetFile, ImageUtils.PNG, picWidth, picHeight);
                if (!imgFile.delete()) {
                    log.warn("❌ 删除失败: " + imgFile.getAbsolutePath());
                }
            }

            if (response.statusCode() == HttpStatus.SUCCESS) {
                log.info("✅ 下载成功: " + filename);
            } else {
                log.warn("❌ 下载失败，HTTP 状态码: " + response.statusCode() + " - " + imageUrl);
            }
            return Optional.of(targetFile);
        } catch (Exception e) {
            log.error("❌ 下载出错:  " + imageUrl, e);
            return Optional.empty();
        }
    }

    private ElementHandle getTitle(Page page) {
        return page.querySelector("//*[@id=\"tbpc-detail-item-title\"]/h1");
    }

    private List<ElementHandle> getMainPics(Page page) {
        ElementHandle mainPicParentElement = page.querySelector("//div[contains(@class, 'thumbnailsWrap')]");
        List<ElementHandle> mainPicElements = mainPicParentElement.querySelectorAll("img");
        mainPicElements = Optional.ofNullable(mainPicElements).orElse(Lists.newArrayList());

        mainPicElements = mainPicElements.stream().filter(imageElement -> {
            Integer width = (Integer) imageElement.evaluate("e => e.naturalWidth");
            Integer height = (Integer) imageElement.evaluate("e => e.naturalHeight");
            return width != null && height != null && width >= 500 && height >= 500;
        }).filter(imageElement -> {
            String src = (String) imageElement.getProperty("src").jsonValue();
            return src != null && !src.isEmpty();
        }).toList();

        return mainPicElements;
    }

}
